//===--- UnqualifiedLookup.cpp - Swift Name Lookup Routines ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// This file implements unqualified lookup, which searches for an identifier
/// from a given context.
///
//===----------------------------------------------------------------------===//

#include "polarphp/ast/AstContext.h"
#include "polarphp/ast/AstVisitor.h"
#include "polarphp/ast/ClangModuleLoader.h"
#include "polarphp/ast/DebuggerClient.h"
#include "polarphp/ast/ExistentialLayout.h"
#include "polarphp/ast/ImportCache.h"
#include "polarphp/ast/Initializer.h"
#include "polarphp/ast/LazyResolver.h"
#include "polarphp/ast/ModuleNameLookup.h"
#include "polarphp/ast/NameLookup.h"
#include "polarphp/ast/NameLookupRequests.h"
#include "polarphp/ast/ParameterList.h"
#include "polarphp/ast/ReferencedNameTracker.h"
#include "polarphp/ast/SourceFile.h"
#include "polarphp/basic/Debug.h"
#include "polarphp/basic/StlExtras.h"
#include "polarphp/basic/SourceMgr.h"
#include "polarphp/basic/Statistic.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

#define DEBUG_TYPE "namelookup"

namespace polar {
using namespace namelookup;

using polar::indices;

namespace {

/// Determine whether unqualified lookup should look at the members of the
/// given nominal type or extension, vs. only looking at type parameters.
template<typename D>
bool shouldLookupMembers(D *decl, SourceLoc loc) {
   // Only look at members of this type (or its inherited types) when
   // inside the body or a protocol's top-level 'where' clause. (Why the
   // 'where' clause? Because that's where you put constraints on
   // inherited associated types.)

   // When we have no source-location information, we have to perform member
   // lookup.
   if (loc.isInvalid() || decl->getBraces().isInvalid())
      return true;

   // Within the braces, always look for members.
   auto &ctx = decl->getAstContext();
   auto braces = decl->getBraces();
   if (braces.start != braces.end &&
       ctx.SourceMgr.rangeContainsTokenLoc(braces, loc))
      return true;

   // Within 'where' clause, we can also look for members.
   if (auto *whereClause = decl->getTrailingWhereClause()) {
      SourceRange whereClauseRange = whereClause->getSourceRange();
      if (whereClauseRange.isValid() &&
          ctx.SourceMgr.rangeContainsTokenLoc(whereClauseRange, loc)) {
         return true;
      }
   }

   // Don't look at the members.
   return false;
}
} // end anonymous namespace

namespace {
class UnqualifiedLookupFactory {

   friend class AstScopeDeclConsumerForUnqualifiedLookup;

public:
   using Flags = UnqualifiedLookupFlags;
   using Options = UnqualifiedLookupOptions;
   using ResultsVector = SmallVector<LookupResultEntry, 4>;

private:
   struct ContextAndResolvedIsCascadingUse {
      DeclContext *const DC;
      const bool isCascadingUse;
   };

   /// Finds lookup results based on the types that self conforms to.
   /// For instance, self always conforms to a struct, enum or class.
   /// But in addition, self could conform to any number of protocols.
   /// For example, when there's a protocol extension, e.g. extension P where
   /// self: P2, self also conforms to P2 so P2 must be searched.
   class ResultFinderForTypeContext {
      UnqualifiedLookupFactory *const factory;
      /// Nontypes are formally members of the base type, i.e. the dynamic type
      /// of the activation record.
      DeclContext *const dynamicContext;
      /// Types are formally members of the metatype, i.e. the static type of the
      /// activation record.
      DeclContext *const staticContext;
      using SelfBounds = SmallVector<NominalTypeDecl *, 2>;
      SelfBounds selfBounds;

   public:
      /// \p staticContext is also the context from which to derive the self types
      ResultFinderForTypeContext(UnqualifiedLookupFactory *factory,
                                 DeclContext *dynamicContext,
                                 DeclContext *staticContext);

      POLAR_DEBUG_DUMP;

   private:
      SelfBounds findSelfBounds(DeclContext *dc);

      // Classify this declaration.
      // Types are formally members of the metatype.
      DeclContext *whereValueIsMember(const ValueDecl *const member) const {
         return isa<TypeDecl>(member) ? staticContext : dynamicContext;
      }

   public:
      /// Do the lookups and add matches to results.
      void findResults(const DeclName &Name, bool isCascadingUse,
                       NLOptions baseNLOptions, DeclContext *contextForLookup,
                       SmallVectorImpl<LookupResultEntry> &results) const;
   };

   enum class AddGenericParameters {
      Yes, No
   };

#ifndef NDEBUG

   /// A consumer for debugging that lets the UnqualifiedLookupFactory know when
   /// finding something.
   class InstrumentedNamedDeclConsumer : public NamedDeclConsumer {
      virtual void anchor() override;

      UnqualifiedLookupFactory *factory;

   public:
      InstrumentedNamedDeclConsumer(UnqualifiedLookupFactory *factory,
                                    DeclName name,
                                    SmallVectorImpl<LookupResultEntry> &results,
                                    bool isTypeLookup)
         : NamedDeclConsumer(name, results, isTypeLookup), factory(factory) {}

      virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
                             DynamicLookupInfo dynamicLookupInfo = {}) override {
         unsigned before = results.size();
         NamedDeclConsumer::foundDecl(VD, Reason, dynamicLookupInfo);
         unsigned after = results.size();
         if (after > before)
            factory->addedResult(results.back());
      }
   };

#endif
   // Inputs
   const DeclName Name;
   DeclContext *const DC;
   ModuleDecl &M;
   const AstContext &Ctx;
   const SourceLoc Loc;
   const SourceManager &SM;

   /// Used to find the file-local names.
   DebuggerClient *const DebugClient;

   const Options options;
   const bool isOriginallyTypeLookup;
   const NLOptions baseNLOptions;
   // Transputs
#ifndef NDEBUG
   InstrumentedNamedDeclConsumer Consumer;
#else
   NamedDeclConsumer Consumer;
#endif
   // Outputs
   SmallVectorImpl<LookupResultEntry> &Results;
   size_t &IndexOfFirstOuterResult;
   ResultsVector UnavailableInnerResults;

#ifndef NDEBUG
   static unsigned lookupCounter;
   static const unsigned targetLookup;
#endif

public: // for exp debugging
   SourceFile const *recordedSF = nullptr;
   bool recordedIsCascadingUse = false;
   unsigned resultsSizeBeforeLocalsPass = ~0;

public:
   // clang-format off
   UnqualifiedLookupFactory(DeclName Name,
                            DeclContext *const DC,
                            SourceLoc Loc,
                            Options options,
                            SmallVectorImpl<LookupResultEntry> &Results,
                            size_t &IndexOfFirstOuterResult);
   // clang-format on

   void performUnqualifiedLookup();

private:
   struct ContextAndUnresolvedIsCascadingUse {
      DeclContext *whereToLook;
      Optional<bool> isCascadingUse;

      ContextAndResolvedIsCascadingUse resolve(const bool resolution) const {
         return ContextAndResolvedIsCascadingUse{
            whereToLook, isCascadingUse.getValueOr(resolution)};
      }
   };

   bool useAstScopesForLookup() const;

   /// For testing, assume this lookup is enabled:
   bool wouldUseAstScopesForLookupIfItWereEnabled() const;

   void lookUpTopLevelNamesInModuleScopeContext(DeclContext *);

   void lookInAstScopes();

   /// Can lookup stop searching for results, assuming hasn't looked for outer
   /// results yet?
   bool isFirstResultEnough() const;

   /// Every time lookup finishes searching a scope, call me
   /// to record the dividing line between results from first fruitful scope and
   /// the result.
   void recordCompletionOfAScope();

   template<typename Fn>
   void ifNotDoneYet(Fn fn) {
      recordCompletionOfAScope();
      if (!isFirstResultEnough())
         fn();
   }

   template<typename Fn1, typename Fn2>
   void ifNotDoneYet(Fn1 fn1, Fn2 fn2) {
      ifNotDoneYet(fn1);
      ifNotDoneYet(fn2);
   }

#pragma mark context-based lookup declarations

   void lookupOperatorInDeclContexts(ContextAndUnresolvedIsCascadingUse);

   void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse);

   void finishLookingInContext(
      AddGenericParameters addGenericParameters,
      DeclContext *lookupContextForThisContext,
      Optional<ResultFinderForTypeContext> &&resultFinderForTypeContext,
      Optional<bool> isCascadingUse);

   void lookupInModuleScopeContext(DeclContext *, Optional<bool> isCascadingUse);

   void lookupNamesIntroducedByPatternBindingInitializer(
      PatternBindingInitializer *PBI, Optional<bool> isCascadingUse);

   void
   lookupNamesIntroducedByLazyVariableInitializer(PatternBindingInitializer *PBI,
                                                  ParamDecl *selfParam,
                                                  Optional<bool> isCascadingUse);

   void lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(
      PatternBindingInitializer *PBI, Optional<bool> isCascadingUse);

   /// An initializer of a global name, or a function-likelocal name.
   void lookupNamesIntroducedByInitializerOfGlobalOrLocal(
      PatternBindingInitializer *PBI, Optional<bool> isCascadingUse);

   void lookupNamesIntroducedByFunctionDecl(AbstractFunctionDecl *AFD,
                                            Optional<bool> isCascadingUse);

   void lookupNamesIntroducedByMemberFunction(AbstractFunctionDecl *AFD,
                                              bool isCascadingUse);

   void lookupNamesIntroducedByPureFunction(AbstractFunctionDecl *AFD,
                                            bool isCascadingUse);

   void lookupNamesIntroducedByClosure(AbstractClosureExpr *ACE,
                                       Optional<bool> isCascadingUse);

   template<typename NominalTypeDeclOrExtensionDecl>
   void lookupNamesIntroducedByNominalTypeOrExtension(
      NominalTypeDeclOrExtensionDecl *D, Optional<bool> isCascadingUse);

   void lookupNamesIntroducedByDefaultArgumentInitializer(
      DefaultArgumentInitializer *I, Optional<bool> isCascadingUse);

   void lookupNamesIntroducedByMiscContext(DeclContext *dc,
                                           Optional<bool> isCascadingUse);

   void lookForLocalVariablesIn(AbstractFunctionDecl *AFD,
                                Optional<bool> isCascadingUse);

   void lookForLocalVariablesIn(ClosureExpr *);

   void lookForLocalVariablesIn(SourceFile *);

   bool isOutsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const;

   void addGenericParametersForContext(DeclContext *dc);

   void addGenericParametersForContext(GenericParamList *);

   /// Consume generic parameters
   void addGenericParametersForFunction(AbstractFunctionDecl *AFD);

   static GenericParamList *getGenericParams(const DeclContext *const dc);

   /// For diagnostic purposes, move aside the unavailables, and put
   /// them back as a last-ditch effort.
   /// Could be cleaner someday with a richer interface to UnqualifiedLookup.
   void setAsideUnavailableResults(size_t firstPossiblyUnavailableResult);

   void recordDependencyOnTopLevelName(DeclContext *topLevelContext,
                                       DeclName name, bool isCascadingUse);

   void addImportedResults(DeclContext *const dc);

   void addNamesKnownToDebugClient(DeclContext *dc);

   void addUnavailableInnerResults();

   void lookForAModuleWithTheGivenName(DeclContext *const dc);

#pragma mark common helper declarations

   static NLOptions
   computeBaseNLOptions(const UnqualifiedLookupOptions options,
                        const bool isOriginallyTypeLookup);

   Optional<bool> getInitialIsCascadingUse() const {
      return options.contains(Flags::KnownPrivate) ? Optional<bool>(false)
                                                   : None;
   }

   static bool resolveIsCascadingUse(const DeclContext *const dc,
                                     Optional<bool> isCascadingUse,
                                     bool onlyCareAboutFunctionBody) {
      return isCascadingUse.getValueOr(dc->isCascadingContextForLookup(
         /*functionsAreNonCascading=*/onlyCareAboutFunctionBody));
   }

   static bool resolveIsCascadingUse(ContextAndUnresolvedIsCascadingUse x,
                                     bool onlyCareAboutFunctionBody) {
      return resolveIsCascadingUse(x.whereToLook, x.isCascadingUse,
                                   onlyCareAboutFunctionBody);
   }

   void findResultsAndSaveUnavailables(
      DeclContext *lookupContextForThisContext,
      ResultFinderForTypeContext &&resultFinderForTypeContext,
      bool isCascadingUse, NLOptions baseNLOptions);

public:
   POLAR_DEBUG_DUMP;

   POLAR_DEBUG_DUMPER (dumpResults());

   POLAR_DEBUG_DUMPER (dumpScopes());

   void printScopes(raw_ostream &OS) const;

   void print(raw_ostream &OS) const;

   void printResults(raw_ostream &OS) const;

   bool verifyEqualTo(const UnqualifiedLookupFactory &&, StringRef thisLabel,
                      StringRef otherLabel) const;

   /// Legacy lookup is wrong here; we should NOT find this symbol.
   bool shouldDiffer() const;

   StringRef getSourceFileName() const;

#ifndef NDEBUG

   bool isTargetLookup() const;

   void stopForDebuggingIfStartingTargetLookup(bool isAstScopeLookup) const;

   void stopForDebuggingIfDuringTargetLookup(bool isAstScopeLookup) const;

   void
   stopForDebuggingIfAddingTargetLookupResult(const LookupResultEntry &) const;

   void addedResult(const LookupResultEntry &) const;

#endif
};

} // namespace

namespace {
/// Used to gather lookup results
class AstScopeDeclConsumerForUnqualifiedLookup
   : public AbstractAstScopeDeclConsumer {
   UnqualifiedLookupFactory &factory;

public:
   AstScopeDeclConsumerForUnqualifiedLookup(UnqualifiedLookupFactory &factory)
      : factory(factory) {}

   virtual ~AstScopeDeclConsumerForUnqualifiedLookup() = default;

   bool consume(ArrayRef<ValueDecl *> values, DeclVisibilityKind vis,
                NullablePtr<DeclContext> baseDC = nullptr) override;

   /// returns true if finished and new value for isCascadingUse
   bool lookInMembers(NullablePtr<DeclContext> selfDC,
                      DeclContext *const scopeDC, NominalTypeDecl *const nominal,
                      function_ref<bool(Optional<bool>)>) override;

#ifndef NDEBUG

   void startingNextLookupStep() override {
      factory.stopForDebuggingIfDuringTargetLookup(true);
   }

   bool isTargetLookup() const override { return factory.isTargetLookup(); }

   void finishingLookup(std::string msg) const override {
      if (isTargetLookup())
         llvm::errs() << "Finishing lookup: " << msg << "\n";
   }

#endif
};
} // namespace

#pragma mark UnqualifiedLookupFactory functions

// clang-format off
UnqualifiedLookupFactory::UnqualifiedLookupFactory(
   DeclName Name,
   DeclContext *const DC,
   SourceLoc Loc,
   Options options,
   SmallVectorImpl<LookupResultEntry> &Results,
   size_t &IndexOfFirstOuterResult)
   :
   Name(Name),
   DC(DC),
   M(*DC->getParentModule()),
   Ctx(M.getAstContext()),
   Loc(Loc),
   SM(Ctx.SourceMgr),
   DebugClient(M.getDebugClient()),
   options(options),
   isOriginallyTypeLookup(options.contains(Flags::TypeLookup)),
   baseNLOptions(computeBaseNLOptions(options, isOriginallyTypeLookup)),
#ifdef NDEBUG
   Consumer(Name, Results, isOriginallyTypeLookup),
#else
   Consumer(this, Name, Results, isOriginallyTypeLookup),
#endif
   Results(Results),
   IndexOfFirstOuterResult(IndexOfFirstOuterResult) {}
// clang-format on

void UnqualifiedLookupFactory::performUnqualifiedLookup() {
#ifndef NDEBUG
   ++lookupCounter;
   auto localCounter = lookupCounter;
   (void) localCounter; // for debugging
#endif
   FrontendStatsTracer StatsTracer(Ctx.Stats, "performUnqualifedLookup",
                                   DC->getParentSourceFile());

   const Optional<bool> initialIsCascadingUse = getInitialIsCascadingUse();

   ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUse{
      DC, initialIsCascadingUse};
   const bool crosscheckUnqualifiedLookup =
      Ctx.LangOpts.CrosscheckUnqualifiedLookup;
   if (useAstScopesForLookup()) {
      static bool haveWarned = false;
      if (!haveWarned && Ctx.LangOpts.WarnIfAstScopeLookup) {
         haveWarned = true;
         llvm::errs() << "WARNING: TRYING Scope exclusively\n";
      }
      lookInAstScopes();
   } else {
#ifndef NDEBUG
      stopForDebuggingIfStartingTargetLookup(false);
#endif

      if (Name.isOperator())
         lookupOperatorInDeclContexts(contextAndIsCascadingUse);
      else
         lookupNamesIntroducedBy(contextAndIsCascadingUse);
   }

   if (crosscheckUnqualifiedLookup &&
       wouldUseAstScopesForLookupIfItWereEnabled()) {
      ResultsVector results;
      size_t indexOfFirstOuterResult = 0;
      UnqualifiedLookupFactory altLookup(Name, DC, Loc, options, results,
                                         indexOfFirstOuterResult);
      if (!useAstScopesForLookup())
         altLookup.lookInAstScopes();
      else if (Name.isOperator())
         altLookup.lookupOperatorInDeclContexts(contextAndIsCascadingUse);
      else
         altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse);

      const auto *AstScopeLabel = "AstScope lookup";
      const auto *contextLabel = "context-bsed lookup";
      const auto *mainLabel =
         useAstScopesForLookup() ? AstScopeLabel : contextLabel;
      const auto *alternateLabel =
         useAstScopesForLookup() ? contextLabel : AstScopeLabel;
      assert(verifyEqualTo(std::move(altLookup), mainLabel, alternateLabel));
   }
}

void UnqualifiedLookupFactory::lookUpTopLevelNamesInModuleScopeContext(
   DeclContext *DC) {
   // TODO: Does the debugger client care about compound names?
   if (Name.isSimpleName() && !Name.isSpecial() && DebugClient &&
       DebugClient->lookupOverrides(Name.getBaseName(), DC, Loc,
                                    isOriginallyTypeLookup, Results))
      return;

   addImportedResults(DC);
   addNamesKnownToDebugClient(DC);
   if (Results.empty()) {
      // If we still haven't found anything, but we do have some
      // declarations that are "unavailable in the current Swift", drop
      // those in.
      addUnavailableInnerResults();
      if (Results.empty())
         lookForAModuleWithTheGivenName(DC);
   }
   recordCompletionOfAScope();
}

bool UnqualifiedLookupFactory::useAstScopesForLookup() const {
   return Ctx.LangOpts.EnableAstScopeLookup &&
          wouldUseAstScopesForLookupIfItWereEnabled();
}

bool UnqualifiedLookupFactory::wouldUseAstScopesForLookupIfItWereEnabled()
const {
   if (!Loc.isValid())
      return false;
   const auto *const SF = DC->getParentSourceFile();
   return SF && SF->isSuitableForAstScopes();
}

#pragma mark context-based lookup definitions

void UnqualifiedLookupFactory::lookupOperatorInDeclContexts(
   const ContextAndUnresolvedIsCascadingUse contextAndUseArg) {
   ContextAndResolvedIsCascadingUse contextAndResolvedIsCascadingUse{
      // Operators are global
      contextAndUseArg.whereToLook->getModuleScopeContext(),
      resolveIsCascadingUse(contextAndUseArg,
         /*onlyCareAboutFunctionBody*/ true)};
   lookupInModuleScopeContext(contextAndResolvedIsCascadingUse.DC,
                              contextAndResolvedIsCascadingUse.isCascadingUse);
}

// TODO: Unify with LookupVisibleDecls.cpp::lookupVisibleDeclsImpl
void UnqualifiedLookupFactory::lookupNamesIntroducedBy(
   const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg) {
#ifndef NDEBUG
   stopForDebuggingIfDuringTargetLookup(false);
#endif
   DeclContext *const dc = contextAndIsCascadingUseArg.whereToLook;
   const auto isCascadingUseSoFar = contextAndIsCascadingUseArg.isCascadingUse;
   if (dc->isModuleScopeContext())
      lookupInModuleScopeContext(dc, isCascadingUseSoFar);
   else if (auto *PBI = dyn_cast<PatternBindingInitializer>(dc))
      lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar);
   else if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc))
      lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar);
   else if (auto *ACE = dyn_cast<AbstractClosureExpr>(dc))
      lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar);
   else if (auto *ED = dyn_cast<ExtensionDecl>(dc))
      lookupNamesIntroducedByNominalTypeOrExtension(ED, isCascadingUseSoFar);
   else if (auto *ND = dyn_cast<NominalTypeDecl>(dc))
      lookupNamesIntroducedByNominalTypeOrExtension(ND, isCascadingUseSoFar);
   else if (auto I = dyn_cast<DefaultArgumentInitializer>(dc))
      lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar);
   else
      lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar);
}

void UnqualifiedLookupFactory::lookupInModuleScopeContext(
   DeclContext *dc, Optional<bool> isCascadingUse) {
   if (auto SF = dyn_cast<SourceFile>(dc)) {
      resultsSizeBeforeLocalsPass = Results.size();
      lookForLocalVariablesIn(SF);
   }
   ifNotDoneYet([&] {
      // If no result has been found yet, the dependency must be on a top-level
      // name, since up to now, the search has been for non-top-level names.
      recordDependencyOnTopLevelName(dc, Name, isCascadingUse.getValueOr(true));
      lookUpTopLevelNamesInModuleScopeContext(dc);
   });
}

void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer(
   PatternBindingInitializer *PBI, Optional<bool> isCascadingUse) {
   // Lazy variable initializer contexts have a 'self' parameter for
   // instance member lookup.
   if (auto *selfParam = PBI->getImplicitSelfDecl())
      lookupNamesIntroducedByLazyVariableInitializer(PBI, selfParam,
                                                     isCascadingUse);
   else if (PBI->getParent()->isTypeContext())
      lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(PBI,
                                                                isCascadingUse);
   else
      lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse);
}

void UnqualifiedLookupFactory::lookupNamesIntroducedByLazyVariableInitializer(
   PatternBindingInitializer *PBI, ParamDecl *selfParam,
   Optional<bool> isCascadingUse) {
   Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter);
   ifNotDoneYet([&] {
      DeclContext *const patternContainer = PBI->getParent();
      // clang-format off
      finishLookingInContext(
         AddGenericParameters::Yes,
         patternContainer,
         ResultFinderForTypeContext(this, PBI, patternContainer),
         resolveIsCascadingUse(PBI, isCascadingUse,
            /*onlyCareAboutFunctionBody=*/false));
      // clang-format on
   });
}

void UnqualifiedLookupFactory::
lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(
   PatternBindingInitializer *PBI, Optional<bool> isCascadingUse) {
   // Initializers for stored properties of types perform static
   // lookup into the surrounding context.
   DeclContext *const storedPropertyContainer = PBI->getParent();
   // clang-format off
   finishLookingInContext(
      AddGenericParameters::Yes,
      storedPropertyContainer,
      ResultFinderForTypeContext(
         this, storedPropertyContainer, storedPropertyContainer),
      resolveIsCascadingUse(storedPropertyContainer, None,
         /*onlyCareAboutFunctionBody=*/false));
   // clang-format on
}

void UnqualifiedLookupFactory::
lookupNamesIntroducedByInitializerOfGlobalOrLocal(
   PatternBindingInitializer *PBI, Optional<bool> isCascadingUse) {
   // There's not much to find here, we'll keep going up to a parent
   // context.
   // clang-format off
   finishLookingInContext(
      AddGenericParameters::Yes,
      PBI,
      None, // not looking in the partic type
      resolveIsCascadingUse(PBI, isCascadingUse,
         /*onlyCareAboutFunctionBody=*/false));
   // clang-format on
}

void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl(
   AbstractFunctionDecl *AFD, Optional<bool> isCascadingUseArg) {

   // DOUG: how does this differ from isOutsideBodyOfFunction below?
   const bool isCascadingUse =
      AFD->isCascadingContextForLookup(false) &&
      (isCascadingUseArg.getValueOr(
         Loc.isInvalid() || AFD->getBodySourceRange().isInvalid() ||
         !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc)));

   if (AFD->getDeclContext()->isTypeContext())
      lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse);
   else
      lookupNamesIntroducedByPureFunction(AFD, isCascadingUse);
}

void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction(
   AbstractFunctionDecl *AFD, bool isCascadingUse) {
   lookForLocalVariablesIn(AFD, isCascadingUse);
   ifNotDoneYet(
      [&] {
         // If we're inside a function context, we're about to move to
         // the parent DC, so we have to check the function's generic
         // parameters first.
         // Cannot start here in finishLookingInContext because AFD's
         // getOuterParameters may be null even when AFD's parent has generics.
         addGenericParametersForFunction(AFD);
      },
      [&] {
         DeclContext *const fnDeclContext = AFD->getDeclContext();
         // If we're not in the body of the function (for example, we
         // might be type checking a default argument expression and
         // performing name lookup from there), the base declaration
         // is the nominal type, not 'self'.
         DeclContext *const BaseDC =
            isOutsideBodyOfFunction(AFD) ? fnDeclContext : AFD;
         // If we are inside of a method, check to see if there are any ivars in
         // scope, and if so, whether this is a reference to one of them.
         // FIXME: We should persist this information between lookups.
         // clang-format off
         finishLookingInContext(
            AddGenericParameters::Yes,
            AFD->getParent(),
            ResultFinderForTypeContext(this, BaseDC, fnDeclContext),
            isCascadingUse);
         // clang-format on
      });
}

void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction(
   AbstractFunctionDecl *AFD, bool isCascadingUse) {
   lookForLocalVariablesIn(AFD, isCascadingUse);
   ifNotDoneYet([&] {
      // clang-format off
      finishLookingInContext(
         AddGenericParameters::Yes,
         AFD,
         None,
         isCascadingUse);
   });
}


void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure(
   AbstractClosureExpr *ACE, Optional<bool> isCascadingUse) {
   if (auto *CE = dyn_cast<ClosureExpr>(ACE))
      lookForLocalVariablesIn(CE);
   ifNotDoneYet([&] {
      // clang-format off
      finishLookingInContext(
         AddGenericParameters::Yes,
         ACE,
         None,
         resolveIsCascadingUse(ACE, isCascadingUse,
            /*onlyCareAboutFunctionBody=*/false));
      // clang-format on
   });
}

template<typename NominalTypeDeclOrExtensionDecl>
void UnqualifiedLookupFactory::lookupNamesIntroducedByNominalTypeOrExtension(
   NominalTypeDeclOrExtensionDecl *D, Optional<bool> isCascadingUse) {
   // clang-format off
   finishLookingInContext(
      AddGenericParameters::Yes,
      D,
      shouldLookupMembers(D, Loc)
      ? Optional<ResultFinderForTypeContext>(
         ResultFinderForTypeContext(this, D, D))
      : None,
      resolveIsCascadingUse(D, isCascadingUse,
         /*onlyCareAboutFunctionBody=*/false));

   // clang-format on
}

void UnqualifiedLookupFactory::
lookupNamesIntroducedByDefaultArgumentInitializer(
   DefaultArgumentInitializer *I, Optional<bool> isCascadingUse) {
   // In a default argument, skip immediately out of both the
   // initializer and the function.
   finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false);
}

void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext(
   DeclContext *dc, Optional<bool> isCascadingUse) {
   // clang-format off
   assert(isa<TopLevelCodeDecl>(dc) ||
          isa<Initializer>(dc) ||
          isa<TypeAliasDecl>(dc) ||
          isa<SubscriptDecl>(dc) ||
          isa<EnumElementDecl>(dc));
   finishLookingInContext(
      AddGenericParameters::Yes,
      dc,
      None,
      resolveIsCascadingUse(DC, isCascadingUse,
         /*onlyCareAboutFunctionBody=*/false));
   // clang-format on
}


void UnqualifiedLookupFactory::finishLookingInContext(
   const AddGenericParameters addGenericParameters,
   DeclContext *const lookupContextForThisContext,
   Optional<ResultFinderForTypeContext> &&resultFinderForTypeContext,
   const Optional<bool> isCascadingUse) {
#ifndef NDEBUG
   stopForDebuggingIfDuringTargetLookup(false);
#endif
   // When a generic has the same name as a member, Swift prioritizes the generic
   // because the member could still be named by qualifying it. But there is no
   // corresponding way to qualify a generic parameter.
   // So, look for generics first.
   if (addGenericParameters == AddGenericParameters::Yes)
      addGenericParametersForContext(lookupContextForThisContext);

   ifNotDoneYet(
      [&] {
         if (resultFinderForTypeContext)
            findResultsAndSaveUnavailables(lookupContextForThisContext,
                                           std::move(*resultFinderForTypeContext),
                                           *isCascadingUse, baseNLOptions);
      },
      // Recurse into the next context.
      [&] {
         lookupNamesIntroducedBy(ContextAndUnresolvedIsCascadingUse{
            lookupContextForThisContext->getParentForLookup(), isCascadingUse});
      });
}


void UnqualifiedLookupFactory::lookForLocalVariablesIn(
   AbstractFunctionDecl *AFD, Optional<bool> isCascadingUse) {
   // Look for local variables; normally, the parser resolves these
   // for us, but it can't do the right thing inside local types.
   // FIXME: when we can parse and typecheck the function body partially
   // for code completion, AFD->getBody() check can be removed.

   if (Loc.isInvalid() || AFD->getBodySourceRange().isInvalid() ||
       !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc) ||
       !AFD->getBody()) {
      return;
   }

   namelookup::FindLocalVal localVal(SM, Loc, Consumer);
   localVal.visit(AFD->getBody());

   ifNotDoneYet([&] {
      if (auto *P = AFD->getImplicitSelfDecl())
         localVal.checkValueDecl(P, DeclVisibilityKind::FunctionParameter);
      localVal.checkParameterList(AFD->getParameters());
   });
}

void UnqualifiedLookupFactory::lookForLocalVariablesIn(ClosureExpr *CE) {
   // Look for local variables; normally, the parser resolves these
   // for us, but it can't do the right thing inside local types.
   if (Loc.isInvalid())
      return;
   namelookup::FindLocalVal localVal(SM, Loc, Consumer);
   if (auto body = CE->getBody())
      localVal.visit(body);
   ifNotDoneYet([&] {
      if (auto params = CE->getParameters())
         localVal.checkParameterList(params);
   });
}

void UnqualifiedLookupFactory::lookForLocalVariablesIn(SourceFile *SF) {
   if (Loc.isInvalid())
      return;
   // Look for local variables in top-level code; normally, the parser
   // resolves these for us, but it can't do the right thing for
   // local types.
   namelookup::FindLocalVal localVal(SM, Loc, Consumer);
   localVal.checkSourceFile(*SF);
}

bool UnqualifiedLookupFactory::isOutsideBodyOfFunction(
   const AbstractFunctionDecl *const AFD) const {
   return !AFD->isImplicit() && Loc.isValid() &&
          AFD->getBodySourceRange().isValid() &&
          !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc);
}

GenericParamList *
UnqualifiedLookupFactory::getGenericParams(const DeclContext *const dc) {
   if (auto nominal = dyn_cast<NominalTypeDecl>(dc))
      return nominal->getGenericParams();
   if (auto ext = dyn_cast<ExtensionDecl>(dc))
      return ext->getGenericParams();
   if (auto subscript = dyn_cast<SubscriptDecl>(dc))
      return subscript->getGenericParams();
   if (auto func = dyn_cast<AbstractFunctionDecl>(dc))
      return func->getGenericParams();
   return nullptr;
}

void UnqualifiedLookupFactory::addGenericParametersForContext(
   DeclContext *dc) {
   // Generics can be nested, so visit the generic list, innermost first.
   // Cannot use DeclContext::forEachGenericContext because this code breaks out
   // if it finds a match and isFirstResultEnough()
   addGenericParametersForContext(getGenericParams(dc));
}

void UnqualifiedLookupFactory::addGenericParametersForContext(
   GenericParamList *dcGenericParams) {
   if (!dcGenericParams)
      return;
   namelookup::FindLocalVal localVal(SM, Loc, Consumer);
   localVal.checkGenericParams(dcGenericParams);
   ifNotDoneYet([&] {
      addGenericParametersForContext(
         dcGenericParams->getOuterParameters());
   });
}

void UnqualifiedLookupFactory::addGenericParametersForFunction(
   AbstractFunctionDecl *AFD) {
   GenericParamList *GenericParams = AFD->getGenericParams();
   if (GenericParams) {
      namelookup::FindLocalVal localVal(SM, Loc, Consumer);
      localVal.checkGenericParams(GenericParams);
   }
}

void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults(
   const DeclName &Name, bool isCascadingUse, NLOptions baseNLOptions,
   DeclContext *contextForLookup,
   SmallVectorImpl<LookupResultEntry> &results) const {
   // An optimization:
   if (selfBounds.empty())
      return;
   const NLOptions options =
      baseNLOptions | (isCascadingUse ? NL_KnownCascadingDependency
                                      : NL_KnownNonCascadingDependency);

   SmallVector<ValueDecl *, 4> Lookup;
   contextForLookup->lookupQualified(selfBounds, Name, options, Lookup);
   for (auto Result : Lookup) {
      results.push_back(LookupResultEntry(whereValueIsMember(Result), Result));
#ifndef NDEBUG
      factory->addedResult(results.back());
#endif
   }
}

// TODO (someday): Instead of adding unavailable entries to Results,
// then later shunting them aside, just put them in the right place
// to begin with.

void UnqualifiedLookupFactory::setAsideUnavailableResults(
   const size_t firstPossiblyUnavailableResult) {
   // An optimization:
   assert(Results.size() >= firstPossiblyUnavailableResult);
   if (Results.size() == firstPossiblyUnavailableResult)
      return;
   // Predicate that determines whether a lookup result should
   // be unavailable except as a last-ditch effort.
   auto unavailableLookupResult = [&](const LookupResultEntry &result) {
      auto &effectiveVersion = Ctx.LangOpts.EffectiveLanguageVersion;
      return result.getValueDecl()->getAttrs().isUnavailableInSwiftVersion(
         effectiveVersion);
   };

   // If all of the results we found are unavailable, keep looking.
   auto begin = Results.begin() + firstPossiblyUnavailableResult;
   if (std::all_of(begin, Results.end(), unavailableLookupResult)) {
      // better to have more structure in results
      UnavailableInnerResults.append(begin, Results.end());
      Results.erase(begin, Results.end());
      return;
   }
   // The debugger may have a different private discriminator
   // in order to support lookup relative to the place where
   // execution is suspended.
   filterForDiscriminator(Results, DebugClient);
}


void UnqualifiedLookupFactory::recordDependencyOnTopLevelName(
   DeclContext *topLevelContext, DeclName name, bool isCascadingUse) {
   recordLookupOfTopLevelName(topLevelContext, Name, isCascadingUse);
   recordedSF = dyn_cast<SourceFile>(topLevelContext);
   recordedIsCascadingUse = isCascadingUse;
}

void UnqualifiedLookupFactory::addImportedResults(DeclContext *const dc) {
   using namespace namelookup;
   SmallVector<ValueDecl *, 8> CurModuleResults;
   auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly
                                                : ResolutionKind::Overloadable;
   lookupInModule(dc, Name, CurModuleResults, NLKind::UnqualifiedLookup,
                  resolutionKind, dc);

   // Always perform name shadowing for type lookup.
   if (options.contains(Flags::TypeLookup)) {
      removeShadowedDecls(CurModuleResults, dc);
   }

   for (auto VD : CurModuleResults) {
      Results.push_back(LookupResultEntry(VD));
#ifndef NDEBUG
      addedResult(Results.back());
#endif
   }

   filterForDiscriminator(Results, DebugClient);
}

void UnqualifiedLookupFactory::addNamesKnownToDebugClient(DeclContext *dc) {
   if (Name.isSimpleName() && DebugClient)
      DebugClient->lookupAdditions(Name.getBaseName(), dc, Loc,
                                   isOriginallyTypeLookup, Results);
}

void UnqualifiedLookupFactory::addUnavailableInnerResults() {
   Results = std::move(UnavailableInnerResults);
}

void UnqualifiedLookupFactory::lookForAModuleWithTheGivenName(
   DeclContext *const dc) {
   using namespace namelookup;
   if (!Name.isSimpleName() || Name.isSpecial())
      return;

   // Look for a module with the given name.
   if (Name.isSimpleName(M.getName())) {
      Results.push_back(LookupResultEntry(&M));
#ifndef NDEBUG
      addedResult(Results.back());
#endif
      return;
   }
   ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier());
   if (!desiredModule && Name == Ctx.TheBuiltinModule->getName())
      desiredModule = Ctx.TheBuiltinModule;
   if (desiredModule) {
      // Make sure the desired module is actually visible from the current
      // context.
      if (Ctx.getImportCache().isImportedBy(desiredModule, dc)) {
         Results.push_back(LookupResultEntry(desiredModule));
#ifndef NDEBUG
         addedResult(Results.back());
#endif
      }
   }
}

#pragma mark common helper definitions


void UnqualifiedLookupFactory::findResultsAndSaveUnavailables(
   DeclContext *lookupContextForThisContext,
   ResultFinderForTypeContext &&resultFinderForTypeContext,
   bool isCascadingUse, NLOptions baseNLOptions) {
   auto firstPossiblyUnavailableResult = Results.size();
   resultFinderForTypeContext.findResults(Name, isCascadingUse, baseNLOptions,
                                          lookupContextForThisContext, Results);
   setAsideUnavailableResults(firstPossiblyUnavailableResult);
}


NLOptions UnqualifiedLookupFactory::computeBaseNLOptions(
   const UnqualifiedLookupOptions options,
   const bool isOriginallyTypeLookup) {
   NLOptions baseNLOptions = NL_UnqualifiedDefault;
   if (options.contains(Flags::AllowInterfaceMembers))
      baseNLOptions |= NL_InterfaceMembers;
   if (isOriginallyTypeLookup)
      baseNLOptions |= NL_OnlyTypes;
   if (options.contains(Flags::IgnoreAccessControl))
      baseNLOptions |= NL_IgnoreAccessControl;
   return baseNLOptions;
}

bool UnqualifiedLookupFactory::isFirstResultEnough()
const {
   return !Results.empty() && !options.contains(Flags::IncludeOuterResults);
}

void UnqualifiedLookupFactory::recordCompletionOfAScope() {
   // OK to call (NOOP) if there are more inner results and Results is empty
   if (IndexOfFirstOuterResult == 0)
      IndexOfFirstOuterResult = Results.size();
}

UnqualifiedLookupFactory::ResultFinderForTypeContext::
ResultFinderForTypeContext(UnqualifiedLookupFactory *factory,
                           DeclContext *dynamicContext,
                           DeclContext *staticContext)
   : factory(factory),
     dynamicContext(dynamicContext), staticContext(staticContext),
     selfBounds(findSelfBounds(staticContext)) {}

UnqualifiedLookupFactory::ResultFinderForTypeContext::SelfBounds
UnqualifiedLookupFactory::ResultFinderForTypeContext::findSelfBounds(
   DeclContext *dc) {
   auto nominal = dc->getSelfNominalTypeDecl();
   if (!nominal)
      return {};

   SelfBounds selfBounds;
   selfBounds.push_back(nominal);

   // For a protocol extension, check whether there are additional "Self"
   // constraints that can affect name lookup.
   if (dc->getExtendedInterfaceDecl()) {
      auto ext = cast<ExtensionDecl>(dc);
      auto bounds = getSelfBoundsFromWhereClause(ext);
      for (auto bound : bounds.decls)
         selfBounds.push_back(bound);
   }
   return selfBounds;
}

#pragma mark AstScopeImpl support

void UnqualifiedLookupFactory::lookInAstScopes() {

   AstScopeDeclConsumerForUnqualifiedLookup consumer(*this);

#ifndef NDEBUG
   stopForDebuggingIfStartingTargetLookup(true);
#endif

   const auto history = AstScope::unqualifiedLookup(DC->getParentSourceFile(),
                                                    Name, Loc, DC, consumer);

   ifNotDoneYet([&] {
      // Copied from lookupInModuleScopeContext
      // If no result has been found yet, the dependency must be on a top-level
      // name, since up to now, the search has been for non-top-level names.
      auto *const moduleScopeContext = DC->getParentSourceFile();

      const Optional<bool> isCascadingUseAtStartOfLookup =
         !Name.isOperator()
         ? getInitialIsCascadingUse()
         : resolveIsCascadingUse(DC, getInitialIsCascadingUse(),
            /*onlyCareAboutFunctionBody*/ true);

      const Optional<bool> isCascadingUseAfterLookup =
         AstScope::computeIsCascadingUse(history, isCascadingUseAtStartOfLookup);

      recordDependencyOnTopLevelName(moduleScopeContext, Name,
                                     isCascadingUseAfterLookup.getValueOr(true));
      lookUpTopLevelNamesInModuleScopeContext(moduleScopeContext);
   });
}

bool AstScopeDeclConsumerForUnqualifiedLookup::consume(
   ArrayRef<ValueDecl *> values, DeclVisibilityKind vis,
   NullablePtr<DeclContext> baseDC) {
   for (auto *value: values) {
      if (factory.isOriginallyTypeLookup && !isa<TypeDecl>(value))
         continue;
      if (!value->getFullName().matchesRef(factory.Name))
         continue;

      // In order to preserve the behavior of the existing context-based lookup,
      // which finds all results for non-local variables at the top level instead
      // of stopping at the first one, ignore results at the top level that are
      // not local variables. The caller \c lookInAstScopes will
      // then do the appropriate work when the scope lookup fails. In
      // FindLocalVal::visitBraceStmt, it sees PatternBindingDecls, not VarDecls,
      // so a VarDecl at top level would not be found by the context-based lookup.
      if (isa<SourceFile>(value->getDeclContext()) &&
          (vis != DeclVisibilityKind::LocalVariable || isa<VarDecl>(value)))
         return false;

      factory.Results.push_back(LookupResultEntry(value));
#ifndef NDEBUG
      factory.stopForDebuggingIfAddingTargetLookupResult(factory.Results.back());
#endif
   }
   factory.recordCompletionOfAScope();
   return factory.isFirstResultEnough();
}

bool AstScopeDeclGatherer::consume(ArrayRef<ValueDecl *> valuesArg,
                                   DeclVisibilityKind,
                                   NullablePtr<DeclContext>) {
   for (auto *v: valuesArg)
      values.push_back(v);
   return false;
}

// TODO: in future, migrate this functionality into AstScopes
bool AstScopeDeclConsumerForUnqualifiedLookup::lookInMembers(
   NullablePtr<DeclContext> selfDC, DeclContext *const scopeDC,
   NominalTypeDecl *const nominal,
   function_ref<bool(Optional<bool>)> calculateIsCascadingUse) {
   if (selfDC) {
      if (auto *d = selfDC.get()->getAsDecl()) {
         if (auto *afd = dyn_cast<AbstractFunctionDecl>(d))
            assert(!factory.isOutsideBodyOfFunction(afd) && "Should be inside");
      }
   }
   auto resultFinder = UnqualifiedLookupFactory::ResultFinderForTypeContext(
      &factory, selfDC ? selfDC.get() : scopeDC, scopeDC);
   const bool isCascadingUse =
      calculateIsCascadingUse(factory.getInitialIsCascadingUse());
   factory.findResultsAndSaveUnavailables(scopeDC, std::move(resultFinder),
                                          isCascadingUse, factory.baseNLOptions);
   factory.recordCompletionOfAScope();
   return factory.isFirstResultEnough();
}

llvm::Expected<LookupResult>
UnqualifiedLookupRequest::evaluate(Evaluator &evaluator,
                                   UnqualifiedLookupDescriptor desc) const {
   SmallVector<LookupResultEntry, 4> results;
   size_t indexOfFirstOuterResult = 0;
   UnqualifiedLookupFactory factory(desc.Name, desc.DC, desc.Loc, desc.Options,
                                    results, indexOfFirstOuterResult);
   factory.performUnqualifiedLookup();
   return LookupResult(results, indexOfFirstOuterResult);
}

#pragma mark debugging
#ifndef NDEBUG

void UnqualifiedLookupFactory::InstrumentedNamedDeclConsumer::anchor() {}

#endif

void UnqualifiedLookupFactory::ResultFinderForTypeContext::dump() const {
   (void) factory;
   llvm::errs() << "dynamicContext: ";
   dynamicContext->dumpContext();
   llvm::errs() << "staticContext: ";
   staticContext->dumpContext();
   llvm::errs() << "selfBounds: ";
   for (const auto *D : selfBounds)
      D->dump(llvm::errs(), 1);
   llvm::errs() << "\n";
}

void UnqualifiedLookupFactory::dump() const { print(llvm::errs()); }

void UnqualifiedLookupFactory::dumpScopes() const { printScopes(llvm::errs()); }

void UnqualifiedLookupFactory::dumpResults() const { printResults(llvm::errs()); }

void UnqualifiedLookupFactory::printScopes(raw_ostream &out) const {
   out << "\n\nScopes:\n";
   DC->getParentSourceFile()->getScope().print(out);
   out << "\n";
}

void UnqualifiedLookupFactory::printResults(raw_ostream &out) const {
   for (auto i : indices(Results)) {
      if (i == resultsSizeBeforeLocalsPass)
         out << "============== next pass ============\n";
      out << i << ": ";
      Results[i].print(out);
      out << "\n";
   }
   if (resultsSizeBeforeLocalsPass == Results.size())
      out << "============== next pass ============\n";
   if (resultsSizeBeforeLocalsPass == ~0u)
      out << "never tried locals\n\n";
}

void UnqualifiedLookupFactory::print(raw_ostream &OS) const {
   OS << "Look up";
#ifndef NDEBUG
   OS << " (" << lookupCounter << ")";
#endif
   OS << " '" << Name << "' at: ";
   Loc.print(OS, DC->getAstContext().SourceMgr);
   OS << "\nStarting in: ";
   DC->printContext(OS);
   OS << "\n";
}

#pragma mark debugging: output utilities for grepping

static void writeLine(std::string s) {
   llvm::errs() << "\n+-+-+-+-  " << s << "\n";
}

StringRef UnqualifiedLookupFactory::getSourceFileName() const {
   return DC->getParentSourceFile()->getFilename();
}

static void writeFirstLine(const UnqualifiedLookupFactory &ul, llvm::Twine s) {
   std::string line =
      std::string("In file: ") + ul.getSourceFileName().str() + ", " + s.str();
   writeLine(line);
}

static void writeInconsistent(const UnqualifiedLookupFactory &me,
                              StringRef thisLabel,
                              const UnqualifiedLookupFactory &other,
                              StringRef otherLabel, llvm::Twine s) {
   writeFirstLine(me, s);
   other.print(llvm::errs());
   llvm::errs() << "\n" << thisLabel << " Results:\n";
   me.printResults(llvm::errs());
   llvm::errs() << "\n" << otherLabel << " Results:\n";
   other.printResults(llvm::errs());
   me.printScopes(llvm::errs());
}

#pragma mark comparing results

bool UnqualifiedLookupFactory::verifyEqualTo(
   const UnqualifiedLookupFactory &&other, const StringRef thisLabel,
   StringRef otherLabel) const {
   if (shouldDiffer()) {
      return true;
   }
   auto writeErr = [&](llvm::Twine s) {
      writeInconsistent(*this, thisLabel, other, otherLabel, s);
   };
   if (Results.size() != other.Results.size()) {
      writeErr(thisLabel + " found " + std::to_string(Results.size()) + " but " +
               otherLabel + " found " + std::to_string(other.Results.size()));
      assert(false && "mismatch in number of results");
   }
   for (size_t i : indices(Results)) {
      const auto &e = Results[i];
      const auto &oe = other.Results[i];
      if (e.getValueDecl() != oe.getValueDecl()) {
         // print_ast_tc_function_bodies.swift generic from subscript vs get fn
         std::string a;
         llvm::raw_string_ostream as(a);
         std::string b;
         llvm::raw_string_ostream bs(b);
         e.getValueDecl()->print(as);
         oe.getValueDecl()->print(bs);
         if (a == b)
            llvm::errs() << "ValueDecls differ but print same\n";
         else {
            writeErr(std::string("ValueDecls differ at ") + std::to_string(i));
            assert(false && "other lookup found different Decl");
         }
      }
      if (e.getDeclContext() != oe.getDeclContext()) {
         writeErr((std::string("Contexts differ at ")) + std::to_string(i));
         assert(false && "AstScopeImpl found different context");
      }
      // unsigned printContext(llvm::raw_ostream &OS, unsigned indent = 0,
      // bool onlyAPartialLine = false) const;
   }
   if (IndexOfFirstOuterResult != other.IndexOfFirstOuterResult) {
      writeErr(std::string("IndexOfFirstOuterResult differs, should be: ")
               + std::to_string(IndexOfFirstOuterResult)
               + std::string(", is: ")
               + std::to_string(other.IndexOfFirstOuterResult));
      assert(false && "other lookup IndexOfFirstOuterResult differs");
   }
   if (recordedSF != other.recordedSF) {
      writeErr(std::string("recordedSF differs: shouldBe: ") +
               (recordedSF ? recordedSF->getFilename().str()
                           : std::string("<no name>")) +
               std::string(" is: ") +
               (other.recordedSF ? other.recordedSF->getFilename().str()
                                 : std::string("<no name>")));
      assert(false && "other lookup recordedSF differs");
   }
   if (recordedSF && recordedIsCascadingUse != other.recordedIsCascadingUse) {
      writeErr(std::string("recordedIsCascadingUse differs: shouldBe: ") +
               std::to_string(recordedIsCascadingUse) + std::string(" is: ") +
               std::to_string(other.recordedIsCascadingUse));
      assert(false && "other lookup recordedIsCascadingUse differs");
   }
   return true;
}

bool UnqualifiedLookupFactory::shouldDiffer() const {
   auto *SF = dyn_cast<SourceFile>(DC->getModuleScopeContext());
   if (!SF)
      return false;

   static std::vector<const char *> testsThatShouldDiffer{
      "swift/test/Constraints/diagnostics.swift",
      "swift/test/Constraints/enum_cases.swift",
      "swift/test/Constraints/rdar39401774.swift",
      "swift/test/Constraints/rdar39401774-astscope.swift",
      "swift/test/Interpreter/repl.swift",
      "swift/test/Sema/diag_defer_captures.swift",
      "swift/test/Sema/diag_use_before_declaration.swift",
      "swift/test/SourceKit/CodeFormat/indent-closure.swift",
      "swift/test/TypeCoercion/overload_noncall.swift",
      "swift/test/expr/capture/nested_class.swift",
      "swift/test/expr/capture/order.swift",
      "swift/test/NameBinding/name-binding.swift"
   };
   StringRef fileName = SF->getFilename();
   return llvm::any_of(testsThatShouldDiffer, [&](const char *testFile) {
      return fileName.endswith(testFile);
   });
}

#pragma mark breakpointing
#ifndef NDEBUG

bool UnqualifiedLookupFactory::isTargetLookup() const {
   return lookupCounter == targetLookup;
}

void UnqualifiedLookupFactory::stopForDebuggingIfStartingTargetLookup(
   const bool isAstScopeLookup) const {
   if (!isTargetLookup())
      return;
   if (isAstScopeLookup)
      llvm::errs() << "starting target AstScopeImpl lookup\n";
   else
      llvm::errs() << "starting target context-based lookup\n";
}

void UnqualifiedLookupFactory::stopForDebuggingIfDuringTargetLookup(
   const bool isAstScopeLookup) const {
   if (!isTargetLookup())
      return;
   if (isAstScopeLookup)
      llvm::errs() << "during target AstScopeImpl lookup\n";
   else
      llvm::errs() << "during target context-based lookup\n";
}

void UnqualifiedLookupFactory::stopForDebuggingIfAddingTargetLookupResult(
   const LookupResultEntry &e) const {
   if (!isTargetLookup())
      return;
   auto &out = llvm::errs();
   out << "\nresult for Target lookup:\n";
   e.print(out);
   out << "\n";
}

void UnqualifiedLookupFactory::addedResult(const LookupResultEntry &e) const {
   stopForDebuggingIfAddingTargetLookupResult(e);
}

unsigned UnqualifiedLookupFactory::lookupCounter = 0;

// set to ~0 when not debugging
const unsigned UnqualifiedLookupFactory::targetLookup = ~0;

#endif // NDEBUG

} // polar
